package assetapi

import (
	"encoding/json"
	"fmt"

	ledgerapi "github.com/hyperledger/fabric-samples/asset-kx/chaincode-go/chaincode/ledger-api"
	"github.com/prometheus/common/log"
	"github.com/shopspring/decimal"
)

const DataType = "ASSET"

//抵质押类型
type PledgeType string

//币种
type CurrencyType string

type Asset interface {
	ledgerapi.StateData
	SetOwner(owner string)
	Freeze(amount string) error
	UnFreeze(amount string) error
	SetValuationAmount(amount string)
	GetValuationAmount() string
	FrozenAmount() string
	String() string
	GetAssetNo() string
	//资产金额
	AssetAmount() string
	GetAssetType() string
}

const (
	//抵押
	PledgeTypeDY PledgeType = "DY"
	//质押
	PledgeTypeZY PledgeType = "ZY"

	//人民币
	RMB CurrencyType = "RMB"
)

var assetFactory = make(map[string]func() Asset)

func init() {
	assetFactory["BusinessTicket"] = func() Asset {
		return &BusinessTicket{}
	}
}

func UnmarshalStateData(content []byte) (ledgerapi.StateData, error) {
	return Unmarshal(content)
}

func Unmarshal(content []byte) (Asset, error) {
	if len(content) == 0 {
		return nil, nil
	}
	baseAsset := &BaseAsset{}
	if err := json.Unmarshal(content, baseAsset); err != nil {
		return nil, err
	}

	if buildAsset, found := assetFactory[baseAsset.AssetType]; found {
		asset := buildAsset()
		if err := json.Unmarshal(content, asset); err != nil {
			return nil, err
		} else {
			return asset, nil
		}
	} else {
		return baseAsset, nil
	}
}

type BaseAsset struct {
	AssetNo    string     `json:"assetNo"`
	Owner      string     `json:"owner"`
	PledgeType PledgeType `json:"pledgeType"`
	//估值
	ValuationAmount string `json:"valuationAmount"`
	//冻结金额
	FreezeAmount string `json:"freezeAmount"`
	CreateTime   string `json:"createTime"`
	//备注
	Remarks   string `json:"remarks"`
	AssetType string `json:"assetType"`
}

func (b *BaseAsset) Keys() []string {
	return []string{b.AssetNo}
}
func (b *BaseAsset) Serialize() ([]byte, error) {
	return json.Marshal(b)
}
func (b *BaseAsset) DataType() string{
	return DataType
}

func (b *BaseAsset) GetAssetType() string {
	return b.AssetType
}

func (b *BaseAsset) AssetAmount() string {
	panic("implement me")
}

func (b *BaseAsset) GetValuationAmount() string {
	return b.ValuationAmount
}

func (b *BaseAsset) GetAssetNo() string {
	return b.AssetNo
}

func (b *BaseAsset) String() string {
	marshal, err := json.Marshal(b)
	if err != nil {
		panic(err)
	}
	return string(marshal)
}

func (b *BaseAsset) FrozenAmount() string {
	return b.FreezeAmount
}

func (b *BaseAsset) SetOwner(owner string) {
	b.Owner = owner
}

func (b *BaseAsset) Freeze(amount string) error {
	amountF, err := decimal.NewFromString(amount)
	if err != nil {
		log.Errorf("parse amount[%s] error", amount)
		return err
	}
	amountFB, err := decimal.NewFromString(b.FreezeAmount)
	if err != nil {
		log.Errorf("parse amount[%s] error", b.FreezeAmount)
		return err
	}
	totalFreezeAmount := amountFB.Add(amountF)
	valuationAmount, err := decimal.NewFromString(b.ValuationAmount)
	if err != nil {
		log.Errorf("parse amount[%s] error", b.ValuationAmount)
		return err
	}
	if totalFreezeAmount.GreaterThan(valuationAmount) {
		log.Errorf("totalFreezeAmount > valuationAmount when freeze, original valuationAmount:%v, totalFreezeAmount:%v", b.ValuationAmount, totalFreezeAmount)
		return fmt.Errorf("totalFreezeAmount > valuationAmount when freeze, original valuationAmount:%v, totalFreezeAmount:%v", b.ValuationAmount, totalFreezeAmount)
	}
	b.FreezeAmount = totalFreezeAmount.String()
	return nil
}

func (b *BaseAsset) UnFreeze(amount string) error {
	amountF, err := decimal.NewFromString(amount)
	if err != nil {
		log.Errorf("parse amount[%s] error", amount)
		return err
	}
	amountFB, err := decimal.NewFromString(b.FreezeAmount)
	if err != nil {
		log.Errorf("parse amount[%s] error", b.FreezeAmount)
		return err
	}
	leftFreezeAmount := amountFB.Sub(amountF)
	if leftFreezeAmount.IsNegative() {
		log.Errorf("leftFreezeAmount < 0 when unFreeze, original freezeAmount:%s, unfreeze amount:%s", b.FreezeAmount, amount)
		return fmt.Errorf("leftFreezeAmount < 0 when unFreeze, original freezeAmount:%s, unfreeze amount:%s", b.FreezeAmount, amount)
	}
	b.FreezeAmount = leftFreezeAmount.String()
	return nil
}

func (b *BaseAsset) SetValuationAmount(amount string) {
	b.ValuationAmount = amount
}

type BusinessTicket struct {
	BaseAsset
	//付款人
	Payer string `json:"payer"`
	//是否电票
	Electronic bool `json:"electronic"`
	//币种
	CurrencyType CurrencyType `json:"currencyType"`
	//付款人开户行名称
	PayerBankName string `json:"payerBankName"`
	//付款人开户行行号
	PayerBankNo string `json:"payerBankNo"`
	//商票金额
	TicketAmount string `json:"ticketAmount"`
	//到期日
	EndDate string `json:"endDate"`
	//出票日
	IssueDate string `json:"issueDate"`
}

func (b *BusinessTicket) AssetAmount() string {
	return b.TicketAmount
}
func (b *BusinessTicket) String() string {
	marshal, err := json.Marshal(b)
	if err != nil {
		panic(err)
	}
	return string(marshal)
}

func (b *BusinessTicket) Serialize() ([]byte, error) {
	return json.Marshal(b)
}